﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace November {
    public partial class NovView : Form, INovView {
        private ScrollablePanel ContentPanel { get; set; }
        private ScalablePictureBox PictureBox { get; set; }
        private IViewMode ViewMode { get; set; }
        private INovPresenter Presenter { get; set; }

        public NovView(INovModel model) {
            InitializeComponent();

            ContentPanel = new ScrollablePanel();
            ContentPanel.Dock = DockStyle.Fill;
            ContentPanel.Location = new Point(0, 0);
            Controls.Add(ContentPanel);

            PictureBox = new ScalablePictureBox();
            PictureBox.Location = new Point(0, 0);
            ContentPanel.Controls.Add(PictureBox);

            ViewMode = new FitToScreenViewMode();

            Presenter = new NovPresenter(this, model);

            PictureBox.MouseClick += OnMouseClick;
            PictureBox.MouseDoubleClick += OnMouseClick;
            (ContentPanel as Control).KeyDown += OnKeyPress;
        }

        public void OnMouseClick(Object sender, MouseEventArgs e) {
            Presenter.OnMouseInput(e.Button, new Point(PictureBox.Left + e.X, PictureBox.Top + e.Y), ContentPanel.Size);
        }

        public void OnKeyPress(Object sender, KeyEventArgs e) {
            Presenter.OnKeyInput(e.KeyCode, e.Shift, e.Control, e.Alt);
        }

        public IViewMode GetViewMode() {
            return ViewMode;
        }

        private delegate void SetTitleDelegate(String title);
        public void SetTitle(String title) {
            if (InvokeRequired) Invoke((SetTitleDelegate)SetTitle, title);
            else Text = title;
        }

        private delegate void SetImageDelegate(Image image);
        public void SetImage(Image image) {
            if (InvokeRequired) Invoke((SetImageDelegate)SetImage, image);
            else {
                PictureBox.Image = image;
                UpdateLayout();
            }
        }

        private delegate void SetViewModeDelegate(IViewMode viewMode);
        public void SetViewMode(IViewMode viewMode) {
            if (InvokeRequired) Invoke((SetViewModeDelegate)SetViewMode, viewMode);
            else {
                ViewMode = viewMode;
                UpdateLayout();
            }
        }

        // http://stackoverflow.com/questions/487661/how-do-i-suspend-painting-for-a-control-and-its-children
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
        private const int WM_SETREDRAW = 11;

        private void SuspendDrawing() {
            SendMessage(Handle, WM_SETREDRAW, false, 0);
        }

        private void ResumeDrawing() {
            SendMessage(Handle, WM_SETREDRAW, true, 0);
            Refresh();
        }

        private void UpdateLayout() {
            SuspendDrawing();

            ContentPanel.HorizontalScroll.Value = ContentPanel.HorizontalScroll.Minimum;
            ContentPanel.VerticalScroll.Value = ContentPanel.VerticalScroll.Minimum;
            PictureBox.Location = new Point(0, 0);

            if (PictureBox.Image != null) {
                PictureBox.Size = ViewMode.UpdateLayout(ContentPanel.Size, PictureBox.Image.Size);
                if (PictureBox.Image.Width <= ContentPanel.Width && PictureBox.Image.Height <= ContentPanel.Height) {
                    PictureBox.SizeMode = PictureBoxSizeMode.CenterImage;
                }
                else {
                    PictureBox.SizeMode = PictureBoxSizeMode.Zoom;
                }
            }

            ResumeDrawing();
        }
    }
}
